package com.huanf.media.service.jobHandler;


import com.huanf.domain.entity.MediaProcess;
import com.huanf.media.service.MediaFilesService;
import com.huanf.media.service.MediaProcessService;
import com.huanf.media.utils.Mp4VideoUtil;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author Administrator
 * 视频处理任务
 */
@Slf4j
@Component
public class VideoTask {

    @Autowired
    MediaProcessService mediaProcessService;

    @Autowired
    MediaFilesService mediaFilesService;

    @Value("${videoprocess.ffmpegpath}")
    String ffmpegpath;


    @XxlJob("videoJobHandler")
    public void videoJobHandler(){
        //分片参数
        //执行器的序号
        int shardIndex = XxlJobHelper.getShardIndex();
        //执行器总数
        int shardTotal = XxlJobHelper.getShardTotal();

        //确定cpu的核心数来开启线程池
        int processors = Runtime.getRuntime().availableProcessors();
        //查询待处理的任务
        List<MediaProcess> mediaProcesses = mediaProcessService.selectListByShardIndex( shardTotal,shardIndex, processors);

        //开启任务
        //使用线程池开启任务 （创建线程池的三种 一池n线程  一池一线程 自动扩容线程池（遇强则强））
        /**
         *  1.  ExecutorService executorService = Executors.newFixedThreadPool(5);
         *         try {
         *             for (int i =0 ;i<10;i++){
         *                 executorService.execute(()->{
         *                     System.out.println(Thread.currentThread().getName()+"执行视频转码任务");
         *                 });
         *             }
         *         } catch (Exception e) {
         *             throw new RuntimeException(e);
         *         } finally {
         *             executorService.shutdown();
         *         }
         *   2. ExecutorService executorService = Executors.newSingleThreadExecutor();
         *
         *   3. ExecutorService executorService = Executors.newCachedThreadPool();
         */
        int size = mediaProcesses.size();
        if (size == 0) {
            log.debug("取到的视频任务处理数量为"+size);
            return;
        }
        ExecutorService executorService = Executors.newFixedThreadPool(processors);
        CountDownLatch latch = new CountDownLatch(size);
        //执行视频转码任务
        try {
            for (MediaProcess mp : mediaProcesses) {
                // 把循环变量拷贝到局部 final 变量里，避免并发问题
                final MediaProcess media = mp;
                executorService.execute(() -> {
                    try {
                        // 4. 先下载视频到本地
                        File source = mediaFilesService.downloadFileFromMinIO(
                                media.getBucket(), media.getFilePath());
                        if (source == null) {
                            log.error("下载视频失败，ID：{}", media.getId());
                            // 直接保存“下载失败”状态
                            mediaProcessService.saveProcessFinishStatus(
                                    media.getId(), "3", media.getFileId(),
                                    null, "下载视频失败");
                            return;
                        }

                        // 5. 抢占并标记“处理中”
                        boolean locked = mediaProcessService.startTask(media.getId());
                        if (!locked) {
                            log.info("任务已被抢走，ID：{}", media.getId());
                            return;
                        }

                        // 6. 转码
                        File mp4File = File.createTempFile("transcode-", ".mp4");
                        String result = new Mp4VideoUtil(
                                ffmpegpath,
                                source.getAbsolutePath(),
                                media.getFileId() + ".mp4",
                                mp4File.getAbsolutePath()
                        ).generateMp4();

                        if (!"success".equals(result)) {
                            log.error("转码失败，ID：{}，原因：{}", media.getId(), result);
                            mediaProcessService.saveProcessFinishStatus(
                                    media.getId(), "3", media.getFileId(),
                                    null, result);
                            return;
                        }

                        // 7. 上传并标记成功
                        if (!mp4File.exists() || !mp4File.isFile()) {
                            log.error("转码后的文件不存在或不是常规文件，ID：{}", media.getId());
                            mediaProcessService.saveProcessFinishStatus(
                                    media.getId(), "3", media.getFileId(),
                                    null, "转码后的文件不存在或不是常规文件");
                            return;
                        }
                        boolean uploaded = mediaFilesService.addMediaFilesTOMinIo(
                                getFilePathByMd5(media.getFileId(), ".mp4"),           // objectName
                                "video/mp4",                  // mimeType
                                mp4File.getAbsolutePath(),    // localFilePath
                                media.getBucket()             // bucket
                        );
                        if (!uploaded) {
                            log.error("上传 MP4 失败，ID：{}", media.getId());
                            mediaProcessService.saveProcessFinishStatus(
                                    media.getId(), "3",
                                    "上传 MP4 失败",null, media.getFileId());
                            return;
                        }

                        String url = getFilePathByMd5(media.getFileId(), ".mp4");
                        mediaProcessService.saveProcessFinishStatus(
                                media.getId(), "2","success", url, media.getFileId());
                        log.info("任务完成，ID：{}", media.getId());

                    } catch (Exception e) {
                        log.error("任务执行异常，ID：" + media.getId(), e);
                        mediaProcessService.saveProcessFinishStatus(
                                media.getId(), "3",
                                media.getFileId(), null, e.getMessage());
                    } finally {
                        // 8. 保证一定会 countDown
                        latch.countDown();
                    }
                });
            }

            // 9. 等待所有子任务完成或超时
            boolean done = latch.await(30, TimeUnit.MINUTES);
            if (!done) {
                log.warn("部分任务未在 30 分钟内完成");
            }
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            log.error("等待子任务被中断", ie);
        } finally {
            // 10. 关闭线程池
            executorService.shutdown();
        }
    }
    private String getFilePathByMd5(String fileMd5, String extension) {
        return fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/" + fileMd5 + extension;
    }
}
